A cikkemhez fűzött megjegyzések Sokan kérték, hogy magyarázzam el, miért olyan borzalmas a cikkben leírt Dockerfile.
Az előző epizód rövid összefoglalója: két fejlesztő szoros határidővel készíti a Dockerfile-t. A folyamat során bejön hozzájuk az Ops, Igor Ivanovics. A végső Dockerfile annyira rossz, hogy az MI már a szívroham határán van.

Most megnézzük, mi a probléma ezzel a Dockerfile-lal.
Tehát eltelt egy hét.
Dev Petya a cafeteriában találkozik egy csésze kávé mellett Ops Igor Ivanovics-sal.
P: Igor Ivanovics, nagyon elfoglalt vagy? Szeretném tisztázni, hol hibáztunk.
II: Ez jó, nem gyakran találkozol olyan fejlesztőkkel, akiket érdekel az üzemeltetés.
Először is, állapodjunk meg néhány dologban:
- A Docker ideológiája: egy konténer — egy folyamat.
- Minél kisebb a konténer, annál jobb.
- Minél többet hozunk a cache-ből, annál jobb.
P: De miért kellene egy konténerben egyetlen folyamatnak lennie?
II: A Docker indításakor figyeli az 1-es pid állapotát. Ha a folyamat meghal, a Docker megpróbálja újraindítani a konténert. Tegyük fel, hogy a konténerben több alkalmazás fut, vagy a fő alkalmazás nem az 1-es pid-vel indul. Ha a folyamat meghal, a Docker erről nem szerez tudomást.
Ha nincsenek további kérdések, mutassátok a Dockerfile-otokat.
És Petya megmutatta:
FROM ubuntu:latest
# A forráskód másolása
COPY . /app
WORKDIR /app
# A csomaglisták frissítése
RUN apt-get update
# Csomagok frissítése
RUN apt-get upgrade
# Szükséges csomagok telepítése
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
# Bundler telepítése
RUN gem install bundler
# A nodejs telepítése, amelyet a statikus fájlok összeszerelésére használnak
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
# Függőségek telepítése
RUN bundle install --without development test --path vendor/bundle
# Cache-ek takarítása
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
# A konténer indításakor futó script, amely elindít mindent.
CMD ["/app/init.sh"]II: Hú, nézzük meg sorban. Kezdjük az első sorral:
FROM ubuntu:latestTe a következő taget veszed, latest. A tag használata latest előre megjósolhatatlan következményekkel jár. Képzeld el, hogy a képet karbantartó új verziót hoz létre más szoftverlistával, ez a kép megkapja a latest taget. És a te konténered a legjobb esetben már nem épül be, a legrosszabb esetben pedig olyan hibákat tapasztalsz, amik korábban nem voltak.
Egy teljes OS képet veszel sok felesleges szoftverrel, ami megnöveli a konténer méretét. Minél több szoftver van benne, annál több rés és sebezhetőség keletkezik.
Ráadásul, minél nagyobb a kép, annál több helyet foglal el a hoszton és a regisztrációs adatbázisban (tudod, valahol csak tárolod a képeket)?
K: Igen, természetesen, van regisztrációnk, te állítottad be.
AI: Szóval, miről is beszéltem?.. Ja igen, a képekről... A hálózati terhelés is nő. Egyetlen képnél ez nem észlelhető, de amikor folyamatosan építesz, tesztelsz és telepítesz, az jelentős. És ha nincs God’s mode-od az AWS-en, akkor még egy hatalmas számla is érkezik.
Ezért a legmegfelelőbb képet kell választani, pontos verzióval és minimális szoftverrel. Például, vedd: FROM ruby:2.5.5-stretch
K: Oh, értem. Hol és hogyan nézhetem meg a meglévő képeket? Hogyan tudom, melyikre van szükségem?
AI: Általában a képeket a , ne keverd össze a Pornhub-bal :). Általában egy képnek több verziója létezik:
Alpine: a képek minimalista Linux alapképre épülnek, mindössze 5 MB. Hátránya: a saját libc megvalósításával épül, a standard csomagok nem működnek benne. A megfelelő csomag keresése és telepítése nem kis időt vesz igénybe.
Scratch: alap kép, más képek előállítására nem használják. Csak bináris, előkészített adatok futtatására szolgál. Tökéletesen alkalmas olyan bináris alkalmazások futtatására, amelyek mindent tartalmaznak, például go-alkalmazásokat.
Valamilyen operációs rendszer alapján, például Ubuntu vagy Debian. Nos, ezt gondolom, nem kell magyaráznom.
AI: Most fel kell telepítenünk az összes kiegészítő csomagot és ki kell takarítanunk a cache-eket. Azonnal el lehet dobni a apt-get upgrade. Ellenkező esetben minden egyes építésnél, függetlenül az alap képként használt rögzített címkétől, eltérő képek keletkeznek. A csomagok frissítése a képben a karbantartó feladata, ez a címke megváltoztatásával jár.
K: Igen, próbáltam megtenni, ezt értem el:
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: Nem rossz, de itt is van mit javítani. Nézd, ez a parancs:
RUN rm -rf /usr/local/bundle/cache/*.gem
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* … nem törli az adatokat a végleges képből, csak létrehoz egy további réteget ezek az adatok nélkül. Helyesen így:
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/* De ez még nem minden. Mi van nálad, Ruby? Akkor nem kell kezdetben az egész projektet másolni. Elég, ha csak a Gemfile-t és a Gemfile.lock-ot másolod.
Ezzel a megközelítéssel a bundle install csak akkor fog futni, ha a Gemfile vagy a Gemfile.lock megváltozott, nem pedig minden forrásszerkesztésnél.
Ugyanezek a módszerek működnek más nyelvek esetében is, amelyek függőségkezelőt használnak, például npm, pip, composer és más, függőséglistát használó rendszerek.
És végül, emlékszel, hogy az elején arról beszéltem, hogy a Docker ideológiája "egy konténer — egy folyamat"? Ez azt jelenti, hogy a supervisor-ra nincs szükség. A systemd telepítése sem indokolt, ugyanazok miatt. Valójában a Docker maga a supervisor. Amikor megpróbálsz több folyamatot futtatni benne, az mintha egyetlen supervisor folyamatban több alkalmazást indítanál.
A build során egy egységes képet készítesz, és majd elindítasz annyi konténert, amennyi szükséges, hogy mindegyikben egy folyamat fusson.
De erről később.
K: Úgy tűnik, megértettem. Nézd, mi jön ki:
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"]A daemonok indítását átkonfiguráljuk a konténer indításakor?
AI: Igen, pontosan. Egyébként használhatod a CMD-t és az ENTRYPOINT-ot is. Kideríteni, mi a különbség, az a házi feladatod. Erről a témáról a Habr-on van egy jó cikk. .
Na, folytassuk. Te letöltöd a node telepítő fájlt, de nincs garancia arra, hogy abban az lesz, amire szükséged van. Hozzá kell adnunk a validációt. Például így:
FUSS 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/* A hash check will allow you to verify that you've downloaded the correct file.
Q: But if the file changes, the build will fail.
AI: Yes, and strangely enough, that’s a plus too. You'll know that the file has changed and can check what has been altered. Who knows, maybe they added a script that deletes everything it touches or creates a backdoor.
Q: Thank you. So the final Dockerfile will look like this:
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"]Q: Igor Ivanovich, thank you for your help. I need to run, I still have to make 10 commits today.
Igor Ivanovich, pausing the hurried colleague with a glance, takes a sip of strong coffee. After pondering for a few seconds about the SLA of 99.9% and code without bugs, he asks a question.
AI: Where do you store your logs?
Q: Of course, in production.log. By the way, how will we access them without ssh?
AI: If you leave them in files, a solution has already been invented for you. The docker exec command allows you to run any command in the container. For example, you can do a cat for the logs. And by using the key -it and starting bash (if it's installed in the container), you will get interactive access to the container.
But it's not advisable to keep logs in files. At the very least, it leads to uncontrolled growth of the container, as logs are never rotated. All logs should be sent to stdout. There, they can already be viewed using the command docker logs.
Q: Igor Ivanovich, how about moving the logs to a mounted directory on the physical node, like user data?
AI: It's good that you remembered to move data stored on the node's disk. Logs can be treated that way too, just don't forget to set up rotation.
Alright, you can run now.
P: Igor Ivanovics, tudna ajánlani valamit, amit érdemes elolvasni?
II: Először olvasd el , valószínűleg senki sem ismeri jobban a Dockert, mint ők.
Ha pedig gyakorlati tapasztalatot szeretnél szerezni, menj el egy Hiszen a tudás gyakorlat nélkül halott.
Forrás: habr.com
