Docker: ikke skadelige tips

I kommentarene til artikkelen min Docker: skadelige råd det har vært mange forespørsel om å forklare hva som er så grusomt med Dockerfile-en som er beskrevet i den.

Kort oppsummering av forrige episode: to utviklere med stramme frister lager Dockerfile. I prosessen kommer Ops Igor Ivanovich inn. Den endelige Dockerfile er så dårlig at AI er nær hjertestans.

Docker: ikke skadelige tips

La oss finne ut hva som er galt med denne Dockerfile.

Så, det har gått en uke.

Utvikler Petya møter Ops Igor Ivanovich i kantinen over en kopp kaffe.

P: Igor Ivanovich, er du veldig opptatt? Jeg vil gjerne finne ut hvor vi gjort feil.

II: Det er bra, du møter sjelden utviklere som er interessert i drift.
For å begynne, la oss bli enige om noen ting:

  1. Docker-ideologien: én container — én prosess.
  2. Jo mindre container, jo bedre.
  3. Jo mer som hentes fra cachen, jo bedre.

P: Men hvorfor må det være én prosess i én container?

II: Docker overvåker tilstanden til prosessen med pid 1 når en container startes. Hvis prosessen dør, prøver Docker å starte containeren på nytt. Anta at du har flere applikasjoner kjørende i containeren, eller at hovedapplikasjonen ikke er startet med pid 1. Hvis prosessen dør, vil ikke Docker vite om det.

Hvis det ikke er flere spørsmål, vis meg Dockerfile-en din.

Og Petya viste:

FROM ubuntu:latest

# Kopierer kildekoden
COPY .\/ \/app
WORKDIR \/app

# Oppdaterer pakkelisten
RUN apt-get update 

# Oppdaterer pakker
RUN apt-get upgrade

# Installerer nødvendige pakker
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor

# Installere bundler
RUN gem install bundler

# Installerer nodejs som brukes for å bygge statikk
RUN curl -sL https:\/\/deb.nodesource.com\/setup_9.x | sudo bash -
RUN apt-get install -y nodejs

# Installerer avhengigheter
RUN bundle install --without development test --path vendor\/bundle

# Rydder opp 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
# Kjører skriptet ved oppstart av containeren, som vil starte alt annet.
CMD ["\/app\/init.sh"]

II: Åh, la oss gå gjennom dette systematisk. La oss starte med første linje:

FROM ubuntu:latest

Du bruker taggen latest. Bruken av taggen latest kan føre til uforutsigbare konsekvenser. Tenk deg at vedlikeholderen av bildet lager en ny versjon med en annen liste over programvare, og dette bildet får taggen latest. Og containeren din slutter i beste fall å kompilere, eller i verste fall får du feil som du ikke hadde før.

Du tar et bilde med et fullt operativsystem med mye unødvendig programvare, som blåser opp størrelsen på containeren. Jo mer programvare, desto flere hull og sårbarheter.

I tillegg, jo større bilde, desto mer plass tar det på verten og i registry (du lagrer bilder et sted, ikke sant)?

P: Ja, selvfølgelig, vi har et registry, du var jo som satte det opp.

AI: Så, hva var det jeg sa?.. Å ja, volumene... Lasten på nettverket øker også. For et enkelt bilde er det ubemerket, men når du har kontinuerlig bygning, testing og distribusjon, er det merkbart. Og hvis du ikke har Gud-modus på AWS, vil du også få en astronomisk regning.

Derfor må du velge det mest passende bildet, med nøyaktig versjon og minimum av programvare. For eksempel, ta: FROM ruby:2.5.5-stretch

P: Å, jeg forstår. Hvordan og hvor kan jeg se tilgjengelige bilder? Hvordan forstår jeg hvilket jeg trenger?

AI: Vanligvis får man bilder fra Docker Hub, ikke forveksle med porn hub :). For et bilde finnes det vanligvis flere bygninger:
Canonical har allerede: bilder er bygget på et minimalistisk Linux-bilde, bare 5 MB. Ulempen er: det er bygget med sin egen implementasjon av libc, standardpakker fungerer ikke. Å finne og installere de nødvendige pakkene vil ta tid.
Scratch: grunnleggende bilde, brukes ikke til å bygge andre bilder. Det er utelukkende ment for å kjøre binære, forberedte data. Ideelt for å kjøre binære applikasjoner som inkluderer alt nødvendig, for eksempel Go-applikasjoner.
Basert på et eller annet operativsystem, for eksempel Ubuntu eller Debian. Jeg tror jeg ikke trenger å forklare mer.

AI: Nå må vi installere alle nødvendige pakker og rydde opp etter oss selv. Og vi kan straks kaste apt-get upgrade. Ellers, ved hver bygging, til tross for den faste taggen på grunnbildet, vil det bli laget forskjellige bilder. Oppdatering av pakker i bildet er en oppgave for vedlikeholderen, og dette medfører endring av taggen.

P: Ja, jeg prøvde å gjøre dette, og det ble slik:

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 verst, men det er også noe å jobbe med her. Se, denne kommandoen:

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 bildet, men oppretter bare et ekstra lag uten disse dataene. Riktig slik:

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. Hva har du der, Ruby? Da er det ikke nødvendig å kopiere hele prosjektet i starten. Det er nok å kopiere Gemfile og Gemfile.lock.

Med denne tilnærmingen vil ikke bundle install bli kjørt ved hver endring av kildekoden, men bare hvis Gemfile eller Gemfile.lock har endret seg.

De samme metodene fungerer også for andre språk med avhengighetsbehandlere, som npm, pip, composer og andre som er basert på en fil med avhengighetsliste.

Og til slutt, husker du at jeg i starten snakket om Docker-ideologien «en beholder — én prosess»? Det betyr at supervisor ikke er nødvendig. Det er heller ikke nødvendig å installere systemd, av de samme grunnene. I praksis er Docker selv en supervisor. Og når du prøver å kjøre flere prosesser i den, er det som å kjøre flere applikasjoner i én prosess med supervisor.
Under byggingen vil du lage ett enkelt bilde, og så kjøre det nødvendige antallet containere, slik at det i hver kjører én prosess.

Men mer om dette senere.

Q: Det ser ut som jeg forstår. Se hva vi får:

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 vi vil overstyre oppstart av demoner når vi starter containeren?

AI: Ja, det stemmer. Forresten kan du bruke både CMD og ENTRYPOINT. Å forstå forskjellen er leksene dine. Det finnes en god artikkel om dette på Habr. article.

Så, la oss gå videre. Du laster ned installasjonsfilen for node, men det er ingen garanti for at det inneholder det du trenger. Vi må legge til validering. For eksempel slik:

Kjø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 kontrollsummen kan du sjekke at du har lastet ned riktig fil.

P: Men hvis filen endres, vil ikke byggingen gå igjennom.

AI: Ja, og merkelig nok er det også en fordel. Du vil vite at filen har endret seg, og kan se hva som har skjedd. Man vet aldri, kanskje de la til et skript som sletter alt det kommer borti, eller lager en bakdør.

P: Takk. Så den endelige Dockerfile vil se slik ut:

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, takk for hjelpen. Jeg må gå nå, jeg må gjøre 10 commits til i dag.

Igor Ivanovich, som stanser den hastige kollegaen med blikket, tar en slurk av den sterke kaffen. Etter å ha tenkt noen sekunder på SLA 99,9 % og feilfri kode, stiller han spørsmålet.

AI: Hvor lagrer dere loggene?

P: Selvfølgelig i production.log. Forresten, hvordan får vi tilgang til dem uten ssh?

AI: Hvis dere lar dem være i filene, har løsningen allerede blitt funnet for dere. Docker exec-kommandoen lar deg utføre hvilken som helst kommando i containeren. For eksempel kan du bruke cat for loggene. Og ved å bruke flagget -it og kjøre bash (hvis det er installert i containeren), får du interaktiv tilgang til containeren.

Men det er ikke lurt å lagre logger i filer. I det minste fører det til ukontrollert vekst av containeren, ingen roterer loggene. Alle logger bør sendes til stdout. Der kan de allerede sees ved hjelp av kommandoen docker logs.

P: Igor Ivanovich, hva med å flytte loggene til en montert katalog, på den fysiske noden, som brukernes data?

AI: Bra du ikke glemte å flytte dataene som er lastet opp til nodens disk. Det kan også gjøres med loggene, men ikke glem å sette opp rotasjon.
Alt, nå kan du gå.

P: Igor Ivanovich, kan du anbefale noe jeg kan lese?

AI: For å begynne, les anbefalingene fra Docker-utviklerne, det er lite trolig at noen kjenner Docker bedre enn dem.

Og hvis du ønsker å få praktisk erfaring, bør du delta på intensivkurset. Teori uten praksis er død.

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster