ИТ развој

Demystify Docker: Ship Reliable Apps with Pragmatic Containerization

Разјаснете го Docker: Испорачувајте сигурни апликации со прагматична контејнеризација

Docker често се претставува како начин да се „спакува апликација“. Тоа е точно, но ја потценува вистинската придобивка: контејнерите ги прават видливи претпоставките околу една апликација. Верзијата на PHP, екстензиите, пакетите на оперативниот систем, конфигурацијата на веб-серверот, командата за queue worker и редоследот на стартување можат да живеат покрај кодот наместо да бидат расфрлани низ wiki-страници и долготрајни сервери.

Таа видливост е она што го прави Docker вреден за backend тимовите. Контејнерот нема автоматски да создаде добра архитектура, брзи барања кон базата на податоци или безбедни распоредувања. Но, многу потешко е да се игнорира околината што навистина му е потребна на вашиот софтвер.

Замислете го контејнерот како извршлив договор

Docker image е непроменлив шаблон. Контејнер е активна инстанца на тој шаблон. Image-от ги опишува датотечниот систем и стандардниот процес; конфигурацијата доставена при извршување обезбедува вредности специфични за околината, како што се акредитивите за базата на податоци, нивоата на логирање и URL-адресите на услугите.

Оваа разлика е важна. Кодот на апликацијата и неговите runtime зависности припаѓаат во image-от. Тајните и поставките специфични за распоредувањето не припаѓаат. Кога овие грижи се мешаат, наводно преносливиот image станува врзан за една околина и потешко се промовира од развој во staging, па во production.

За PHP API, корисен договор може да гласи: оваа услуга извршува PHP-FPM, ги вклучува потребните PHP екстензии, го содржи кодот на апликацијата, слуша преку својот process manager и очекува URL за база на податоци и cache endpoint при извршување. Тој договор е доволно конкретен за лаптоп на развивач и production платформа да го почитуваат на различни начини.

Почнете со мал, експлицитен PHP image

Првиот Dockerfile треба да биде едноставен. Едноставното е добро: на рецензентите им дава јасен увид во тоа што влегува во runtime-от и зошто.

FROM php:8.3-fpm

WORKDIR /var/www/app

RUN docker-php-ext-install pdo_mysql

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --no-scripts

COPY . .

RUN composer dump-autoload --optimize --no-dev

CMD ["php-fpm"]

Овој пример претпоставува апликација што користи MySQL преку PDO. Ако на апликацијата ѝ е потребна друга екстензија, додајте ја намерно и проверете ги нејзините барања за изградба. Избегнувајте да инсталирате широка колекција пакети „за секој случај“. Секоја зависност ја зголемува големината на image-от, работата за закрпување и бројот на начини на кои изградбата може да не успее.

Редоследот на инструкциите е намерен. Composer manifest-ите се копираат пред остатокот од изворниот код за Docker да може повторно да го искористи слојот за инсталирање зависности кога се менува само кодот на апликацијата. Тоа ги прави локалната итерација и континуираната интеграција забележливо помалку расипнички, без да ја замагли изградбата.

Во production image, зависностите само за развој се исклучени. Тоа не е само оптимизација: алатките наменети за локално дебагирање не треба тивко да станат production runtime барања. Ако за распоредувањето се потребни build assets, генериран код или загревање на кешот, направете ги тие чекори експлицитни и осигурете се дека можат да се извршуваат без потпирање на локални датотеки што никогаш не биле копирани во image-от.

Составете услуги, но држете ги нивните одговорности одвоени

Повеќето апликации не се еден процес. На типичен backend му треба HTTP влезна точка, база на податоци, кеш и можеби worker. Docker Compose е корисен во развојот бидејќи бележи како се поврзуваат тие услуги без да се преправа дека се една машина.

services:
  app:
    build: .
    environment:
      DATABASE_URL: mysql://app:secret@db:3306/app
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8
    environment:
      MYSQL_DATABASE: app
      MYSQL_USER: app
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: root-secret
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10
    volumes:
      - mysql-data:/var/lib/mysql

volumes:
  mysql-data:

Апликацијата се поврзува со db, а не со localhost, бидејќи секоја услуга има сопствен network namespace. Ова е едно од најчестите рани изненадувања. Внатре во контејнер, localhost значи самиот тој контејнер.

Проверката на здравјето е исто така позначајна од едноставно правило за редослед на стартување. Процесот на база на податоци може да постои пред да може да прифаќа врски. И покрај тоа, кодот на апликацијата сепак треба да се справува со привремени неуспеси при поврзувањето: проверките на здравјето на зависностите го подобруваат однесувањето при стартување, но не ги елиминираат прекините на мрежата, рестартирањата на базата на податоци или настаните за одржување.

Направете стартувањето безбедно за повторување

Оркестрацијата на контејнери очекува процесите да запираат и стартуваат. Распоредувањата ги заменуваат инстанците. Платформите повторно ги закажуваат. Worker може да биде рестартиран по пад. Тоа значи дека задачите при стартување имаат потреба од внимателни граници.

Миграциите на шемата се класичен пример. Нивното автоматско извршување во секој веб-контејнер може да создаде трки кога повеќе реплики стартуваат истовремено. Побезбеден пристап е миграциите да се извршуваат како еден контролиран чекор на распоредување, користејќи го истиот изграден image и истата runtime конфигурација како апликацијата. Самите миграции треба да бидат дизајнирани да толерираат повторни обиди каде што е практично, а големите промени на податоци треба да се планираат одделно од распоредување што опслужува барања.

Истиот принцип важи и за queue-јата. Извршувајте queue worker како посебна услуга со јасна команда, ограничување на ресурси, политика за повторни обиди и однесување за благодатно гасење. Не криејте worker зад shell команда во заднина во веб-контејнерот. Docker може добро да надгледува еден примарен процес; комбинирањето неповрзани долготрајни процеси ги прави логовите, сигналите, скалирањето и неуспесите двосмислени.

Користете контејнери за подобрување на операциите, не само за воведување

Container image станува највреден кога артефактот тестиран во континуираната интеграција е артефактот што се распоредува. Изградете го еднаш, означете го непроменливо, извршете тестови против него каде што е соодветно, а потоа промовирајте го токму тој image. Повторното градење од истата Git ревизија во секоја околина воведува променливи што можат да се избегнат: складиштата на пакети се менуваат, base image-ите се ажурираат и изградбата можеби не е толку репродуцибилна како што изгледа.

Набљудливоста треба да го следи истиот прагматичен пристап. Запишувајте ги логовите на апликацијата на стандарден излез и стандарден излез за грешки за runtime-от да може да ги собира. Чувајте ја конфигурацијата во environment variables или во одобрен механизам за конфигурација. Испраќајте сигнали за прекин до процесот на апликацијата и оставете му време да ја заврши активната работа. Овие избори се едноставни, но ги прават неуспесите дијагностицибилни кога се случуваат надвор од машината на развивачот.

  • Користете датотека .dockerignore за да исклучите директориуми со зависности, тест артефакти, локална конфигурација и други датотеки што не му се потребни на image-от.
  • Извршувајте ја апликацијата како корисник што не е root кога base image-от и распоредувањето го дозволуваат тоа.
  • Фиксирајте ги мааните верзии на base image-ите и прегледувајте ги ажурирањата како дел од редовното одржување.
  • Чувајте ги трајните податоци во управувано складирање или именувани volumes, никогаш во запишливиот датотечен систем на контејнерот.
  • Документирајте ги малкуте команди што навистина им се потребни на развивачите, како build, test, migration и shell access.

Прагматичната мерка за успех

Docker не е значка за инженерска зрелост. Корисен е кога ја намалува неизвесноста: нов развивач може сигурно да го стартува стекот, CI job се извршува во очекуваниот runtime, а production добива артефакт чија содржина е разбрана.

Одржувајте го Dockerfile читлив, image-от фокусиран, услугите одвоени и чекорите за распоредување експлицитни. Кога контејнерите откриваат кревка зависност или нејасен редослед на стартување, третирајте го тоа како корисна повратна информација за системот. Најсилната стратегија за контејнери не е најразработената; тоа е онаа што прави сигурната испорака да изгледа рутински.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.