In den Kommentaren zu meinem Docker- Artikel : Schlechte Ratschläge gab es viele Anfragen zu erklären, warum die darin beschriebene Docker- Datei so schrecklich ist.
Zusammenfassung der vorherigen Serie : Zwei Entwickler in einer schwierigen Frist bilden das Dockerfile. Dabei kommt Ops Igor Ivanovich zu ihnen. Das resultierende Dockerfile ist so schlecht, dass die KI kurz vor einem Herzinfarkt steht.

Lassen Sie uns nun herausfinden, was mit dieser Docker-Datei nicht stimmt.
Es ist also eine Woche vergangen.
Dev Petya trifft sich im Speisesaal mit einer Tasse Kaffee mit Ops Igor Ivanovich.
P: Igor Ivanovich, bist du sehr beschäftigt? Ich würde gerne herausfinden, wo wir es vermasselt haben.
AI: Das ist gut, man trifft nicht oft Entwickler, die sich für den Betrieb interessieren.
Lassen Sie uns zunächst einige Dinge vereinbaren:
- Docker-Ideologie: ein Container - ein Prozess.
- Je kleiner der Behälter, desto besser.
- Je mehr Cache belegt ist, desto besser.
P: Und warum sollte es einen Prozess in einem Container geben?
AI: Docker überwacht beim Starten des Containers den Status des Prozesses mit pid 1. Wenn der Prozess stirbt, versucht Docker, den Container neu zu starten. Angenommen, in Ihrem Container werden mehrere Anwendungen ausgeführt oder die Hauptanwendung wird nicht mit pid 1 ausgeführt. Wenn der Prozess abbricht, weiß Docker nichts davon.
Wenn Sie keine weiteren Fragen haben, zeigen Sie Ihre Docker-Datei.
Und Petja zeigte:
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: Oh, lass es uns in der richtigen Reihenfolge klären. Beginnen wir mit der ersten Zeile:
FROM ubuntu:latest
Sie nehmen das latest
Tag. Die Verwendung des latest
Tags führt zu unvorhersehbaren Konsequenzen. Stellen Sie sich vor, der Image-Betreuer erstellt eine neue Version des Images mit einer anderen Softwareliste. Dieses Image erhält das neueste Tag. Und im besten Fall hört Ihr Container auf zu sammeln, und im schlimmsten Fall fangen Sie Fehler, die vorher nicht existierten.
Sie nehmen ein Bild mit einem vollständigen Betriebssystem mit viel unnötiger Software auf, wodurch der Container aufgeblasen wird. Und je mehr Software, desto mehr Lücken und Schwachstellen.
Je größer das Image, desto mehr Speicherplatz benötigt es auf dem Host und in der Registrierung (speichern Sie die Images irgendwo)?
P: Ja, natürlich haben wir eine Registrierung, und Sie haben sie eingerichtet.
II: Also, wovon spreche ich? .. Oh ja, Volumen ... Die Netzwerklast wächst ebenfalls. Für ein einzelnes Image ist dies unsichtbar, aber bei kontinuierlicher Montage, Tests und Bereitstellung ist dies spürbar. Und wenn Sie in AWS keinen Gottesmodus haben, erhalten Sie auch ein Space-Konto.
Daher müssen Sie das am besten geeignete Bild mit der genauen Version und der minimalen Software auswählen. Nehmen Sie zum Beispiel: FROM ruby:2.5.5-stretch
P: Oh, ich verstehe. Und wie und wo sehen Sie die verfügbaren Bilder? Wie kann ich verstehen, welches ich brauche?
AI: Normalerweise werden Bilder von einem Dockhub aufgenommen , nicht mit einem Pornhub verwechseln :). Es gibt normalerweise mehrere Baugruppen für ein Bild:
Alpine : Images werden auf einem minimalistischen Linux-Image mit nur 5 MB kompiliert. Das Minus: Es wurde mit einer eigenen Implementierung von libc erstellt, Standardpakete funktionieren nicht. Das Finden und Installieren des richtigen Pakets nimmt viel Zeit in Anspruch.
Scratch : Basisbild, das nicht zum Erstellen anderer Bilder verwendet wird. Es ist ausschließlich dazu gedacht, binäre, vorbereitete Daten auszuführen. Ideal zum Ausführen von Binäranwendungen, die alles enthalten, was Sie benötigen, z. B. Go-Anwendungen.
Basierend auf jedem Betriebssystem wie Ubuntu oder Debian. Nun, hier, denke ich, muss ich es nicht erklären.
II: Jetzt müssen wir alles extra setzen. Pakete und reinigen Sie die Caches. Und sofort können Sie apt-get Upgrade werfen. Andernfalls werden mit jeder Baugruppe trotz des festen Tags des Basisbilds unterschiedliche Bilder erhalten. Das Aktualisieren von Paketen in einem Image ist Aufgabe des Betreuers und wird von einer Tag-Änderung begleitet.
P: Ja, ich habe es versucht, es stellte sich so heraus:
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/*
II: Nicht schlecht, aber es gibt auch etwas zu arbeiten. Schauen Sie, hier ist dieser Befehl:
RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
... löscht keine Daten aus dem endgültigen Bild, sondern erstellt nur eine zusätzliche Ebene ohne diese Daten. Richtig so:
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/*
Das ist aber noch nicht alles. Was hast du da, Ruby? Dann ist es nicht notwendig, das gesamte Projekt zu Beginn zu kopieren. Kopieren Sie einfach Gemfile und Gemfile.lock.
Bei diesem Ansatz wird die Bundle-Installation nicht für jede Änderung an der Quelle ausgeführt, sondern nur, wenn sich Gemfile oder Gemfile.lock geändert haben.
Dieselben Methoden funktionieren für andere Sprachen mit dem Abhängigkeitsmanager, z. B. npm, pip, composer und andere, basierend auf der Datei mit der Liste der Abhängigkeiten.
Und schließlich, erinnern Sie sich, am Anfang habe ich über die Docker-Ideologie „Ein Container - ein Prozess“ gesprochen? Dies bedeutet, dass kein Supervisor benötigt wird. Installieren Sie systemd aus den gleichen Gründen auch nicht. In der Tat ist Docker selbst ein Supervisor. Wenn Sie versuchen, mehrere Prozesse darin auszuführen, ist dies wie das Starten mehrerer Anwendungen in einem Supervisor-Prozess.
Beim Zusammenstellen erstellen Sie ein einzelnes Image und führen dann die gewünschte Anzahl von Containern aus, sodass jeder Prozess einen Prozess hat.
Aber dazu später mehr.
P: Es scheint zu verstehen. Sehen Sie, was passiert:
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"]
Werden die Dämonen neu definiert, wenn der Container startet?
AI: Ja, das stimmt. Sie können übrigens sowohl CMD als auch ENTRYPOINT verwenden. Und um zu verstehen, was der Unterschied ist, sind dies Ihre Hausaufgaben. Zu diesem Thema gibt es in Habré einen guten Artikel .
Also komm schon. Sie laden die Datei herunter, um den Knoten zu installieren, es gibt jedoch keine Garantie dafür, dass er das enthält, was Sie benötigen. Es ist notwendig, eine Validierung hinzuzufügen. Zum Beispiel so:
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/*
Mit der Prüfsumme können Sie überprüfen, ob Sie die richtige Datei heruntergeladen haben.
P: Wenn sich die Datei ändert, funktioniert die Assembly nicht.
II: Ja, und seltsamerweise ist dies auch ein Plus. Sie werden feststellen, dass sich die Datei geändert hat, und Sie können sehen, was dort geändert wurde. Man weiß nie, fügten sie beispielsweise ein Skript hinzu, das alles entfernt, was es erreicht, oder eine Hintertür macht.
P: Danke. Es stellt sich heraus, dass die endgültige Docker-Datei folgendermaßen aussehen wird:
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, danke für die Hilfe. Es ist Zeit für mich zu rennen, ich muss für heute 10 weitere Commits machen.
Igor Iwanowitsch, der seinen hastigen Kollegen mit einem Blick aufhält, trinkt einen Schluck starken Kaffee. Nachdem er einige Sekunden über die 99,9% SLA und den Code ohne Fehler nachgedacht hat, stellt er eine Frage.
AI: Und wo bewahren Sie die Protokolle auf?
P: Natürlich in Production.log. Übrigens, ja, wie können wir ohne ssh darauf zugreifen?
II: Wenn Sie sie in den Dateien belassen, wurde bereits eine Lösung für Sie erfunden. Mit dem Befehl docker exec können Sie jeden Befehl in einem Container ausführen. Sie können beispielsweise eine Katze für Protokolle erstellen. Mit dem Schalter -it und dem Ausführen von bash (sofern es im Container installiert ist) erhalten Sie interaktiven Zugriff auf den Container.
Sie sollten jedoch keine Protokolle in Dateien speichern. Zumindest führt dies zu einem unkontrollierten Wachstum des Containers, aber niemand dreht die Protokolle. Alle Protokolle müssen in stdout geworfen werden. Dort können Sie sie bereits mit dem Befehl docker logs sehen .
P: Igor Ivanovich, aber können die Protokolle als Benutzerdaten in das bereitgestellte Verzeichnis auf dem physischen Knoten gestellt werden?
AI: Es ist gut, dass Sie nicht vergessen haben, die auf die Knotenfestplatte heruntergeladenen Daten zu entfernen. Bei Protokollen ist dies ebenfalls möglich. Denken Sie daran, die Rotation einzurichten.
Alles was du laufen kannst.
P: Igor Ivanovich, aber raten Sie, was zu lesen?
AI: Lesen Sie zuerst die Empfehlungen der Entwickler von Docker , kaum jemand kennt Docker besser als sie.
Und wenn Sie durch die Praxis gehen wollen, gehen Sie intensiv . Eine Theorie ohne Praxis ist schließlich tot.