Надвор од контејнерите: Архитектирање на Docker за одржлива отпорност на задниот дел од системот
Docker може да направи backend-от да изгледа прекрасно едноставен: спакувајте ја апликацијата, стартувајте неколку сервиси и испорачајте. Таа едноставност е вредна, но токму таму многу системи прерано престануваат да размислуваат. Контејнерот е механизам за испорака, а не стратегија за отпорност.
Одржливата отпорност на backend-от произлегува од експлицитно дефинирање на границите околу контејнерите: како сервисите стартуваат, откажуваат, закрепнуваат, комуницираат, складираат состојба и се развиваат. Docker помага да се спроведат тие граници. Не ги избира наместо вас.
Почнете со неуспехот, не со Dockerfile
Корисно архитектонско прашање не е „Дали ова може да работи во контејнер?“ Речиси сè може. Наместо тоа, прашајте: „Што се случува кога оваа зависност е бавна, недостапна, рестартирана или испорачува неочекувани податоци?“ Одговорот треба да биде разбирлив без читање на секоја линија од кодот на апликацијата.
За PHP API, тоа значи раздвојување на одговорностите. Веб-процесот треба да обработува барања. Worker за редици треба да обработува асинхрона работа. Закажаните задачи треба да се извршуваат како независно управувани процеси. PostgreSQL или MySQL треба да ги поседува трајните релациски податоци. Redis може да поддржува кеширање, сесии, ограничување на стапката или редици, но не треба тивко да стане единствената копија на критичната деловна состојба.
Овие разлики се важни бидејќи секоја компонента откажува различно. Рестартирањето на PHP worker обично е безбедно ако работата е идемпотентна. Рестартирањето на база на податоци е оперативен настан со импликации за конекции, закрепнување и интегритет на податоците. Третирањето на двете како заменливи „сервиси“ во една Compose-датотека може да ја сокрие разликата сè додека production не ја открие.
Градете мали слики, но дизајнирајте пошироки договори
Production-сликата треба да го содржи она што му е потребно на процесот за извршување, а не целата развојна околина. Повеќефазните build-ови често се чист начин да се задржат build-алатките и развојните зависности надвор од конечната слика.
FROM composer:2 AS dependencies
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
FROM php:8.3-fpm-alpine
WORKDIR /var/www
COPY --from=dependencies /app/vendor ./vendor
COPY . .
CMD ["php-fpm"]
Ова намерно е само почетна точка. Вистинската апликација може да бара PHP екстензии, веб-сервер или reverse proxy, дозволи за датотеки и build-чекор за frontend-ресурси. Важниот принцип е репродуктивноста: сликата треба да се составува од декларирани влезови, а распоредувањето треба да користи конкретна верзија на сликата наместо повторно да гради од изменлива гранка на сервер.
Минимализмот на сликата сам по себе не создава доверливост. Поширокиот договор е конфигурацијата да пристигнува преку механизми специфични за околината, тајните да не се вградуваат во слоеви, логовите да одат на стандарден излез или стандардна грешка и процесот правилно да одговара на сигнали за прекинување. Docker ги прави овие практики природни; отпорниот дизајн ги прави неодложни.
Направете го редоследот на стартување проблем на подготвеност
Честа замка е претпоставката дека стартуван контејнер е подготвена зависност. Процесот на база на податоци може да работи додека сè уште закрепнува, применува иницијализациска работа или одбива конекции. Редицата може да прифати TCP-конекција, но сè уште да не биде употреблива за наменетата операција на апликацијата.
Затоа, стартувањето на апликацијата треба да толерира привремени неуспеси на зависностите. Користете ограничени повторни обиди со backoff, јасно логирање на грешки и режим на неуспех што му овозможува на оркестраторот да го рестартира процесот кога закрепнувањето не е можно. Не потпирајте се само на фиксни паузи како sleep(10); тие го претвораат променливото време на стартување во повторувачка тркачка состојба.
Health check-овите треба да ја одразуваат одлуката што ја поддржуваат. Основна liveness-проверка одговара дали процесот е жив. Readiness-проверка одговара дали безбедно може да прима сообраќај. За API, readiness може да потврди дека е вчитана суштинската конфигурација и дека може да се воспостави конекција со база на податоци. Не треба да извршува скап извештаен query или да зависи од тоа секоја опционална интеграција да е здрава.
Одржувајте ги повторните обиди безбедни
Повторните обиди можат да зголемат инцидент ако секое барање постојано го активира истиот скап downstream повик. Поставете разумни тајмаути, ограничете ги обидите и разликувајте привремени неуспеси од грешки при валидација. Неправилно формулирано барање треба веднаш да врати клиентска грешка; привремено недостапен давател на плаќања може да оправда повторен обид или асинхрона патека за закрепнување.
Кога запишувате во надворешни системи, користете идемпотентни клучеви или правила за единственост на ниво на апликација каде што доменот го дозволува тоа. Ако worker-от падне откако испратил барање, но пред да го забележи успехот, повторниот обид не смее тивко да создаде второ наплатување, е-порака или нарачка.
Контејнерите треба да бидат заменливи; податоците не
Најчистото оперативно правило е едноставно: бришењето и повторното создавање на контејнер на апликацијата не смее да доведе до губење на деловни податоци. Поставените датотеки, записите во базата на податоци, генерираните извози и состојбата на пораките сите имаат потреба од намерно определени места.
За бази на податоци, користете трајно складирање управувано со истото внимание како и самата база на податоци: резервни копии, тестирање на обновување, контрола на пристап, мониторинг и планирање на капацитет. Монтиран volume е перзистентност, не план за резервни копии. Ако обновувањето никогаш не било тестирано во изолирана околина, организацијата има претпоставка наместо процедура за закрепнување.
За поставување датотеки, избегнувајте врзување на трајните податоци за животниот циклус на веб-контејнерот. Чувајте ги датотеките во соодветен траен сервис или стратегија за volume-и, задржете ги метаподатоците во базата на податоци и дефинирајте што се случува кога складиштето е привремено недостапно. API-то треба да врати значаен неуспех, а не да пријави успех пред датотеката да има трајна дестинација.
Користете Compose за јасност, не како архитектонски дијаграм
Docker Compose е одличен за локален развој и може да биде корисен и во помали контролирани распоредувања. Неговата најголема вредност често е комуникативна: ги декларира сервисите, мрежите, volume-ите и очекувањата за околината потребни за извршување на системот.
services:
api:
image: example-api:1.4.0
environment:
APP_ENV: production
DATABASE_URL: ${DATABASE_URL}
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Оваа конфигурација не ја отстранува потребата од обработка на повторни обиди на ниво на апликација, стратегија за резервни копии или управување со тајни. Таа едноставно прави дел од договорот за извршување видлив. Третирајте ја како извршлива документација, а потоа додадете оперативна документација за миграции, враќања назад, одговор на инциденти и обновување на податоци.
Распоредувајте ги промените како повратни операции
Отпорноста е највидлива при промена. Нова слика може да вклучува миграција на шема, промена на конфигурација или ажурирање на зависност што се однесува различно под production-сообраќај. Планот за распоредување треба да одговори на три прашања пред објавување: што се менува, како се потврдува здравјето и како се обновува претходната позната добра состојба?
Миграциите на базата на податоци заслужуваат посебна претпазливост. Претпочитајте адитивни, наназад компатибилни промени кога е можно: додадете nullable колона, распоредете код што може да ги обработи двата облика, пополнете ги податоците, а потоа отстранете го старото однесување во подоцнежно издание. Поврзувањето на неповратна промена на шема со едно распоредување на апликацијата го прави враќањето назад многу поопасно.
- Верзионирајте ги сликите непроменливо и запишете ја распоредeната верзија.
- Извршувајте ги миграциите како експлицитен, набљудлив чекор.
- Поставете ограничувања на ресурси врз основа на измереното однесување на апликацијата.
- Изложете структурирани логови и значајни метрики на апликацијата.
- Вежбајте обновување на податоци и враќање назад на изданија пред итна ситуација да го побара тоа.
Контејнерот е почеток на дисциплината
Docker го заслужува своето место со тоа што го прави софтверот пренослив и границите меѓу процесите конкретни. Зрелата употреба на Docker не е директориум полн со дефиниции на контејнери; тоа е backend што може да се рестартира, набљудува, менува и обновува без да зависи од среќа или недокументирани рачни чекори.
Тоа е трајната промена во перспективата. Градете контејнери што лесно се заменуваат, сервиси што искрено зборуваат за неуспехот на зависностите и патеки за податоци дизајнирани за закрепнување. Кога овие одлуки се на место, Docker престанува да биде архитектурата и станува она што треба да биде: доверлива основа за неа.