I kommentarfeltet til min artikel var der mange anmodninger om at forklare, hvad der er så forfærdeligt ved den beskrevne Dockerfile.
Kort resume af den forrige episode: to udviklere arbejder intenst under en stram deadline for at lave en Dockerfile. I processen kommer Ops Igor Ivanovich forbi. Den endelige Dockerfile er så dårlig, at AI står på randen af et hjertestop.

Lad os nu finde ud af, hvad der er galt med denne Dockerfile.
Så, en uge er gået.
Dev Petya mødes i kantinen over en kop kaffe med Ops Igor Ivanovich.
P: Igor Ivanovich, er du meget optaget? Jeg ville gerne finde ud af, hvor vi gik galt.
II: Det er godt, man møder ikke ofte udviklere, der er interesseret i drift.
For at starte, lad os blive enige om nogle punkter:
- Docker-ideologien: én container — én proces.
- Jo mindre containeren er, jo bedre.
- Jo mere der hentes fra cachen, jo bedre.
P: Men hvorfor skal der være én proces i en container?
II: Docker overvåger tilstanden af processen med pid 1, når en container startes. Hvis processen dør, prøver Docker at genstarte containeren. Antag, at du har flere applikationer kørende i containeren, eller at hovedapplikationen ikke kører med pid 1. Hvis processen dør, får Docker ikke besked.
Hvis der ikke er flere spørgsmål, så vis mig jeres Dockerfile.
Og Petya viste:
FROM ubuntu:latest
# Kopierer kildekoden
COPY .\/ \/app
WORKDIR \/app
# Opdaterer pakken liste
RUN apt-get update
# Opdaterer pakker
RUN apt-get upgrade
# Installerer nødvendige pakker
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
# Installerer bundler
RUN gem install bundler
# Installerer nodejs som bruges til at opbygge statiske filer
RUN curl -sL https:\/\/deb.nodesource.com\/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
# Installerer afhængigheder
RUN bundle install --without development test --path vendor\/bundle
# Rydder op efter sig selv i cachen
RUN rm -rf \/usr\/local\/bundle\/cache\/*.gem
RUN apt-get clean
RUN rm -rf \/var\/lib\/apt\/lists\/* \/tmp\/* \/var\/tmp\/*
RUN rake assets:precompile
# Kører scriptet ved containerstart, som vil starte alt andet.
CMD ["\/app\/init.sh"]II: Åh, lad os tage det en krokodille ad gangen. Lad os begynde med den første linje:
FROM ubuntu:latestDu tager tag latest. Brug af tag latest kan føre til uforudsigelige konsekvenser. Forestil dig, at vedligeholderen af billedet opbygger en ny version af billedet med en anden liste over software, og dette billede får tag latest. Og i bedste fald stopper din container med at bygge, eller i værste fald får du bugs, der ikke var der før.
Du tager et billede med et komplet OS med en masse unødvendig software, der opblæser containerens størrelse. Jo mere software, desto flere svagheder og sårbarheder.
Desuden, jo større billedet er, desto mere plads optager det på værten og i registry (du gemmer jo billederne et sted)?
P: Ja, selvfølgelig, vi har registry, det er jo dig, der satte det op.
AI: Nå, hvad var det nu, jeg talte om?... Åh, ja, størrelser... Belasting på netværket stiger også. For et enkelt billede er det usynligt, men når der er konstant opbygning, test og implementering, bliver det mærkbart. Og hvis du ikke har God’s mode på AWS, så vil regningen også være astronomisk.
Derfor er det vigtigt at vælge det mest passende billede, med den præcise version og minimum af software. For eksempel, tag: FROM ruby:2.5.5-stretch
P: Åh, jeg forstår. Hvordan og hvor kan jeg se de tilgængelige billeder? Hvordan finder jeg ud af, hvilket jeg har brug for?
AI: Som regel findes billederne på , bland ikke sammen med Pornhub :). Der findes normalt flere builds for et billede:
annonceret klarheden af installationspakkerne til Ubuntu 20.04 LTS, testet i: billederne er bygget på et minimalistisk Linux-billede, kun 5 MB. Ulempen er, at det er bygget med en egen implementation af libc, standardpakker fungerer ikke i det. Det tager tid at finde og installere den nødvendige pakke.
Scratch: grundbillede, der ikke bruges til at bygge andre billeder. Det er udelukkende beregnet til at køre binære, forberedte data. Det er ideelt til at køre binære applikationer, der inkluderer alt nødvendigt, som for eksempel Go-applikationer.
På basis af et eller andet OS, som Ubuntu eller Debian. Her behøver jeg ikke at uddybe.
AI: Nu skal vi installere alle de ekstra pakker og rydde op i vores caches. Og du kan straks kaste apt-get upgrade. Ellers vil der, på trods af den faste tag på det grundlæggende billede, være forskellige billeder ved hver opbygning. At opdatere pakker i billedet er en opgave for vedligeholderen, og det indebærer en ændring af taggen.
P: Ja, jeg har prøvet at gøre det, jeg fik det til at se sådan ud:
WORKDIR /app
COPY ./ /app
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash -
&& apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs
&& gem install bundler
&& bundle install --without development test --path vendor/bundle
RUN rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*AI: Ikke dårligt, men der er også noget at arbejde med her. Se, denne kommando:
RUN rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* … sletter ikke data fra det endelige billede, men skaber blot et ekstra lag uden disse data. Sådan skal det være:
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash -
&& apt-get -y install libpq-dev imagemagick gsfonts nodejs
&& gem install bundler
&& bundle install --without development test --path vendor/bundle
&& rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* Men det er ikke alt. Hvad har du der, Ruby? Så er der ikke behov for at kopiere hele projektet i starten. Det er tilstrækkeligt at kopiere Gemfile og Gemfile.lock.
Med denne tilgang vil bundle install ikke blive udført ved hver ændring i koden, men kun hvis Gemfile eller Gemfile.lock er ændret.
De samme metoder fungerer også for andre sprog med afhængighedsstyringssystemer som npm, pip, composer og andre baseret på en fil med afhængighedsliste.
Og til sidst, husker du, at jeg i starten talte om Docker-ideologien "en container — en proces"? Det betyder, at supervisor ikke er nødvendigt. Ligeledes bør du ikke installere systemd af samme årsager. Faktisk er Docker en supervisor i sig selv. Og når du forsøger at køre flere processer derinde, er det ligesom at køre flere apps i en enkelt proces med en supervisor.
Når du bygger, vil du lave et enkelt billede, og så køre det nødvendige antal containere, så der kører en proces i hver.
Men det er noget, vi kan tale om senere.
Q: Det ser ud til, at jeg forstår. Se hvad der sker:
FROM ruby:2.5.5-stretch
WORKDIR /app
COPY Gemfile* /app
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash -
&& apt-get -y install libpq-dev imagemagick gsfonts nodejs
&& gem install bundler
&& bundle install --without development test --path vendor/bundle
&& rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY . /app
RUN rake assets:precompile
CMD ["bundle", "exec", "passenger", "start"]Og vil vi omdefinere opstart af dæmoner når vi starter containeren?
AI: Ja, det er korrekt. Forresten kan både CMD og ENTRYPOINT bruges. At finde ud af forskellen er din lektie. Der er en god artikel om dette emne på Habr. .
Okay, lad os komme videre. Du downloader installationsfilen til node, men der er ingen garanti for, at den indeholder det, du har brug for. Vi skal tilføje validering. For eksempel sådan her:
KØR curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x
&& echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c -
&& bash setup_9.x
&& rm -rf setup_9.x
&& apt-get -y install libpq-dev imagemagick gsfonts nodejs
&& gem install bundler
&& bundle install --without development test --path vendor/bundle
&& rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* Ved hjælp af kontrolsummen kan du verificere, at du har downloadet den rigtige fil.
P: Men hvis filen ændrer sig, så vil bygningen ikke gå igennem.
AI: Ja, og mærkeligt nok er det også en fordel. Du vil vide, at filen er ændret, og du kan se, hvad der er blevet ændret. Hvem ved, måske er der tilføjet et script, der sletter alt, hvad det kan nå, eller laver et bagdør.
P: Tak. Så hvordan vil den endelige Dockerfile se ud:
FROM ruby:2.5.5-stretch
WORKDIR /app
COPY Gemfile* /app
RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x
&& echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c -
&& bash setup_9.x
&& rm -rf setup_9.x
&& apt-get -y install libpq-dev imagemagick gsfonts nodejs
&& gem install bundler
&& bundle install --without development test --path vendor/bundle
&& rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY . /app
RUN rake assets:precompile
CMD ["bundle", "exec", "passenger", "start"]P: Igor Ivanovich, tak for hjælpen. Jeg skal allerede afsted, jeg skal have lavet 10 commits i dag.
Igor Ivanovich, som stopper sin travle kollega med blikket, tager en slurk af sin stærke kaffe. Efter at have tænk på SLA 99.9% og bugfri kode i et par sekunder, stiller han et spørgsmål.
AI: Hvor gemmer I logfilerne?
P: Selvfølgelig i production.log. Apropos, hvordan får vi adgang til dem uden ssh?
AI: Hvis du lader dem være i filerne, er der allerede en løsning til dig. Kommandoen docker exec giver dig mulighed for at udføre en hvilken som helst kommando i containeren. For eksempel kan du gøre cat for loggene. Og ved at bruge nøglen -it og starte bash (hvis det er installeret i containeren), får du interaktiv adgang til containeren.
Men du bør ikke gemme logfilerne i filer. Mindst fordi det fører til ukontrolleret vækst af containeren, da ingen roterer loggene. Alle logfiler skal sendes til stdout. Der kan de allerede ses med kommandoen docker logs.
P: Igor Ivanovich, hvad med at flytte loggene til en monteret mappe, på den fysiske node, ligesom brugerdata?
AI: Godt, at du ikke glemte at flytte dataene, der er gemt på node-disken. Det kan også gøres med loggene, men glem ikke at konfigurere rotationen.
Det er alt, du kan løbe.
P: Igor Ivanovich, kan du anbefale noget at læse?
AI: For at starte, læs , det er usandsynligt, at nogen kender Docker bedre end dem.
Og hvis du vil have praktisk erfaring, så deltag i . Teori uden praksis er død.
Kilde: habr.com
