Docker: ikke dårligt råd

I kommentarerne til min artikel Docker: dårligt råd der var mange anmodninger om at forklare, hvorfor Dockerfilen beskrevet i den var så forfærdelig.

Resumé af forrige serie: To udviklere komponerer en Dockerfile under en stram deadline. I processen kommer Ops Igor Ivanovich til dem. Den resulterende Dockerfile er så dårlig, at AI er på randen af ​​et hjerteanfald.

Docker: ikke dårligt råd

Lad os nu finde ud af, hvad der er galt med denne Dockerfile.

Så er der gået en uge.

Dev Petya møder Ops Igor Ivanovich i spisestuen over en kop kaffe.

P: Igor Ivanovich, har du meget travlt? Jeg vil gerne finde ud af, hvor vi har rodet.

AI: Det er godt, man møder ikke ofte udviklere, der er interesserede i udnyttelse.
Lad os først blive enige om et par ting:

  1. Docker-ideologi: én beholder - én proces.
  2. Jo mindre beholderen er, jo bedre.
  3. Jo mere du tager fra cachen, jo bedre.

P: Hvorfor skal der være én proces i én container?

AI: Docker overvåger, når en container starter, processens tilstand med pid 1. Hvis processen dør, forsøger Docker at genstarte containeren. Lad os sige, at du har flere applikationer kørende i en container, eller at hovedapplikationen ikke kører med pid 1. Hvis processen dør, vil Docker ikke vide noget om det.

Hvis du ikke har yderligere spørgsmål, bedes du vise os din Dockerfile.

Og Petya viste:

FROM ubuntu:latest

# Копируем исходный код
COPY ./ /app
WORKDIR /app

# Обновляем список пакетов
RUN apt-get update 

# Обновляем пакеты
RUN apt-get upgrade

# Устанавливаем нужные пакеты
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor

# Устанавливаем bundler
RUN gem install bundler

# Устанавливаем nodejs используется для сборки статики
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs

# Устанавливаем зависимости
RUN bundle install --without development test --path vendor/bundle

# Чистим за собой кэши
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
# Запускаем скрипт, при старте контейнера, который запустит все остальное.
CMD ["/app/init.sh"]

AI: Åh, lad os tage det i rækkefølge. Lad os starte med den første linje:

FROM ubuntu:latest

Du tager mærket latest. Brug af et tag latest fører til uforudsigelige konsekvenser. Forestil dig, billedvedligeholderen bygger en ny version af billedet med en anden liste over software, dette billede modtager det seneste tag. Og din container holder i bedste fald op med at samle sig, og i værste fald fanger du fejl, der ikke eksisterede før.

Du tager et billede med et fuldgyldigt OS med en masse unødvendig software, som puster beholderens volumen op. Og jo mere software, jo flere huller og sårbarheder.

Derudover, jo større billedet er, jo mere plads fylder det på værten og i registreringsdatabasen (gemmer du billeder et sted)?

P: Ja, selvfølgelig, vi har et register, du sætter det op.

AI: Så hvad taler jeg om?.. Åh ja, mængderne... Belastningen på netværket vokser også. For et enkelt billede er dette ikke mærkbart, men når der er en kontinuerlig opbygning, test og implementering, er det mærkbart. Og hvis du ikke har Guds tilstand på AWS, får du også en kosmisk regning.

Derfor skal du vælge det bedst egnede billede med den nøjagtige version og minimum software. Tag for eksempel: FROM ruby:2.5.5-stretch

P: Åh, jeg kan se. Hvordan og hvor kan jeg se de tilgængelige billeder? Hvordan ved jeg, hvilken jeg skal bruge?

AI: Normalt er billeder taget fra dockerhub, forveksle ikke med pornhub :). Der er normalt flere samlinger til et billede:
Alpine: billeder er samlet på et minimalistisk Linux-billede, kun 5 MB. Dens ulempe: den er kompileret med sin egen libc-implementering, standardpakker virker ikke i den. At finde og installere den nødvendige pakke vil tage meget tid.
Skrab: basisbillede, bruges ikke til at bygge andre billeder. Det er udelukkende beregnet til at køre binære, forberedte data. Ideel til at køre binære applikationer, der inkluderer alt, hvad du har brug for, såsom GO-applikationer.
Baseret på et hvilket som helst operativsystem, såsom Ubuntu eller Debian. Nå, jeg tror ikke, der er behov for at forklare.

AI: Nu skal vi installere alt ekstraudstyret. pakker og ryd dine caches. Og du kan smide det væk med det samme apt-get opgradering. Ellers vil der blive opnået forskellige billeder med hver build, på trods af det faste tag på basisbilledet. Opdatering af pakker i billedet er vedligeholderens opgave og er ledsaget af ændring af tagget.

P: Ja, jeg prøvede at gøre det, det blev sådan her:

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 på. Se, her er 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 opretter kun et ekstra lag uden disse data. Korrekt sådan her:

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å behøver du ikke kopiere hele projektet i starten. Det er nok at kopiere Gemfile og Gemfile.lock.

Med denne tilgang vil bundle-installation ikke blive udført for hver kildeændring, men kun hvis Gemfile eller Gemfile.lock er ændret.

De samme metoder fungerer for andre sprog med en afhængighedsmanager, såsom npm, pip, composer og andre baseret på en fil med en liste over afhængigheder.

Og husk endelig, at jeg i begyndelsen talte om Docker-ideologien "én beholder - én proces"? Det betyder, at der ikke er behov for en supervisor. Du bør heller ikke installere systemd af samme årsager. I det væsentlige er Docker selv en supervisor. Og når du forsøger at køre flere processer i det, er det som at køre flere applikationer i en supervisorproces.
Når du bygger, vil du lave et enkelt billede og derefter starte det nødvendige antal containere, så der kører en proces i hver.

Men mere om det senere.

P: Jeg tror, ​​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"]

Kan vi tilsidesætte lanceringen af ​​dæmoner, når vi starter containeren?

AI: Ja, det er rigtigt. Du kan i øvrigt bruge både CMD og ENTRYPOINT. Og at finde ud af, hvad forskellen er, er dit hjemmearbejde. Der er en god en om dette emne på Habré artiklen.

Så lad os komme videre. Du downloader en fil for at installere 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:

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/* 

Ved hjælp af kontrolsummen kan du bekræfte, at du har downloadet den korrekte fil.

P: Men hvis filen ændres, vil bygningen mislykkes.

AI: Ja, og mærkeligt nok er dette også et plus. Du vil vide, at filen er ændret, og du vil kunne se, hvad der blev ændret der. Man ved aldrig, tilføjede de f.eks. et script, der sletter alt, hvad det kan nå, eller skaber en bagdør.

P: Tak skal du have. Det viser sig, at den endelige Dockerfile vil se sådan 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 din hjælp. Det er tid for mig at løbe, jeg skal lave 10 commits mere i dag.

Igor Ivanovich, der stopper sin forhastede kollega med sit blik, tager en tår stærk kaffe. Efter at have tænkt et par sekunder over 99.9% SLA og fejlfri kode, stiller han et spørgsmål.

AI: Hvor opbevarer du logfilerne?

P: Selvfølgelig i production.log. Forresten, ja, men hvordan kan vi få adgang til dem uden ssh?

AI: Hvis du efterlader dem i filerne, er der allerede opfundet en løsning til dig. Docker exec-kommandoen giver dig mulighed for at udføre enhver kommando i en container. For eksempel kan du lave kat til logs. Og ved at bruge nøglen -det og at køre bash (hvis installeret i containeren) vil give dig interaktiv adgang til containeren.

Men du bør ikke gemme logfiler i filer. Dette fører som minimum til ukontrolleret vækst af beholderen, og ingen roterer stokkene. Alle logfiler skal sendes til stdout. Der kan de allerede ses ved hjælp af kommandoen docker logs.

P: Igor Ivanovich, måske kan jeg lægge logfilerne i en monteret mappe, på en fysisk node, som brugerdata?

AI: Det er godt, at du ikke har glemt at fjerne de data, der er indlæst på nodens disk. Du kan også gøre dette med logs, bare glem ikke at konfigurere rotation.
Det er det, du kan løbe.

P: Igor Ivanovich, kan du rådgive mig, hvad jeg skal læse?

AI: Først, læs anbefalinger fra Docker-udviklere, næppe nogen kender Docker bedre end dem.

Og hvis du vil i praktik, så gå til intensiv. Teori uden praksis er jo død.

Kilde: www.habr.com

Tilføj en kommentar